package org.ala.tiktools.rpc.kcp.protocol.tools;

import org.ala.tiktools.rpc.kcp.config.RpcKcpConfigInfo;
import org.ala.tiktools.rpc.kcp.protocol.Kcp;

/**
 * 拥塞窗口
 *
 * @author ala
 * @date 2025-04-13 13:07
 */
public class KcpCongestion {
	
	
    /**
     *  绑定的kcp
     */
    protected Kcp kcp;
    /**
     *  拥塞窗口
     */
    protected int cwnd;
    /**
     *  拥塞窗口字节数
     */
    protected int incr;
    /**
     *  拥塞阈值
     */
    protected int ssthresh;
    /**
     *  单体Segment，数据段最大字节数
     */
    protected int mss;
    /**
     *  是否开启拥塞控制
     */
    protected boolean nocwnd;


    public KcpCongestion(Kcp kcp, int cwnd, int incr, int ssthresh, int mss) {
        this.kcp = kcp;
        this.cwnd = cwnd;
        this.incr = incr;
        this.ssthresh = ssthresh;
        this.mss = mss;
        this.nocwnd = false;
    }


    /**
     *  返回当前拥塞窗口
     */
    public int cwnd() {
        int cwnd = Math.min(kcp.getSender().getSndWnd(), kcp.getRmtWnd());
        if (!nocwnd) {
            cwnd = Math.min(this.cwnd, cwnd);
        }
        return cwnd;
    }


    /**
     *  更新拥塞窗口
     */
    public void updateCWnd(long oldSndUna, int rmtWnd) {
        if (kcp.getSender().getSndUna() > oldSndUna) {
            updateCWnd(rmtWnd);
        }
    }
    /**
     *  根据当前远程窗口，更新拥塞窗口
     */
    private void updateCWnd(int rmtWnd) {
        if (cwnd < rmtWnd) {
            //  探测期：每次拥塞窗口+1
            if (cwnd < ssthresh) {
                cwnd++;
                incr += mss;
            }
            //  平稳期：拥塞窗口缓慢自增
            else {
                if (incr < mss) {
                    incr = mss;
                }
                incr += (mss * mss) / incr + (mss / 16);
                if ((cwnd + 1) * mss <= incr) {
                    cwnd++;
                }
            }
            //  拥塞窗口不能超过远程窗口
            if (cwnd > rmtWnd) {
                cwnd = rmtWnd;
                incr = rmtWnd * mss;
            }
        }
    }
    
    
    /**
     *  更新拥塞窗口阈值
     */
    public void updateSsThresh(int resent) {
        int inflight = (int) (kcp.getSender().getSndNxt() - kcp.getSender().getSndUna());
        ssthresh = inflight / 2;
        if (ssthresh < RpcKcpConfigInfo.INSTANCE.getiKcpThreshMin()) {
            ssthresh = RpcKcpConfigInfo.INSTANCE.getiKcpThreshMin();
        }
        cwnd = ssthresh + resent;
        incr = cwnd * mss;
    }
    /**
     *  重置拥塞窗口
     */
    public void resetCwnd(int cwnd) {
        ssthresh = cwnd / 2;
        if (ssthresh < RpcKcpConfigInfo.INSTANCE.getiKcpThreshMin()) {
            ssthresh = RpcKcpConfigInfo.INSTANCE.getiKcpThreshMin();
        }
        cwnd = 1;
        incr = mss;
    }
    /**
     *  确保拥塞窗口最小就是1
     */
    public void cwndMin1() {
        if (cwnd < 1) {
            cwnd = 1;
            incr = mss;
        }
    }


    public int getCwnd() {
        return cwnd;
    }

    public void setCwnd(int cwnd) {
        this.cwnd = cwnd;
    }

    public int getIncr() {
        return incr;
    }

    public void setIncr(int incr) {
        this.incr = incr;
    }

    public boolean isNocwnd() {
        return nocwnd;
    }

    public void setNocwnd(boolean nocwnd) {
        this.nocwnd = nocwnd;
    }
}
